﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.IO;
using Util.Media.Flash;

namespace Util.Media
{
    public class SwfImage : GdiImageSequence
    {
        public SwfImage(Stream stream)
        {
            var swf = new Flash.FlashFile(stream);
            int delay = Math.Max(1, (int)(1000.0 / swf.Header.FrameRate));
            Size sz = swf.Header.FrameBounds.Size.ToSize();
            //foreach (Flash.DefineBitsLossless2Tag tag in
            //         swf.Tags.Where(x => x is Flash.DefineBitsLossless2Tag))
            foreach (Flash.Tag tag in swf.Tags)
            {
                var tagBitmap = tag as Flash.DefineBitsLossless2Tag;
                if (tagBitmap != null)
                {
                    using (Bitmap bmp = tagBitmap.GetBitmap())
                    {
                        Bitmap bmpResized = new Bitmap(bmp, sz);
                        base.AddFrame(bmpResized, delay, true);
                    }
                }
            }
        }
    }

    public static class SwfImageEncoder
    {
        /// <summary>
        /// Saves the given multi-frame image to a file in SWF format.
        /// </summary>
        public static void Encode(MultiFrameImage image, string fileName)
        {
            using (Stream outputStream = new FileStream(
                   fileName, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                Encode(image, outputStream);
            }
        }

        /// <summary>
        /// Saves the given multi-frame image to a stream in SWF format.
        /// The delay of the current frame is used as the frame rate of
        /// all frames.
        /// </summary>
        public static void Encode(MultiFrameImage image, Stream stream)
        {
            int width = image.Width;
            int height = image.Height;

            // Compute the FPS. If all frames have the same delay 'd', then
            // FPS is just 1/d. If the frames have different delays, we need
            // to find a way to mimic that.
            float fps = (float)(1.0 / image.FrameDelay.TotalSeconds);

            // Encode a SWF file.
            FlashFile swf = new FlashFile();

            FileHeader swfHeader = new FileHeader();
            swfHeader.Version = 6;
            swfHeader.IsCompressed = false;
            swfHeader.FileSize = 0; // To be filled later

            if (image.FrameCount > 0x7FFF)
                throw new NotSupportedException("Too many frames.");
            swfHeader.FrameCount = (ushort)image.FrameCount;
            swfHeader.FrameRate = fps;
            swfHeader.FrameBounds = new RectangleF(0, 0, width, height);

            swf.Header = swfHeader;

            // Add tags.
            List<Tag> tags = new List<Tag>();
            tags.Add(new SetBackgroundColorTag { Color = Color.Black });
            for (int i = 0; i < image.FrameCount; i++)
            {
                image.FrameIndex = i;

                ushort bitmapId = (ushort)(2 * i + 1);
                ushort shapeId = (ushort)(2 * i + 2);

                var tagBitmap = new DefineBitsLossless2Tag();
                tagBitmap.CharacterId = bitmapId;
                tagBitmap.SetBitmap(image.Frame as Bitmap);
                tags.Add(tagBitmap);

                var tagShape = new DefineShape3Tag
                {
                    ShapeId = shapeId,
                    ShapeBounds = new RectangleF(0, 0, width, height),
                    FillStyles = new FillStyle[] 
                    {
                        new BitmapFillStyle 
                        {
                            BitmapId = bitmapId,
                            BitmapMatrix = new Matrix(20, 0, 0, 20, 0, 0), // to twips
                            Type = FillStyleType.ClippedBitmap,
                        }
                    },
                    LineStyles = null,
                    ShapeRecords = new ShapeRecord[] 
                    {
                        new StyleChangeRecord 
                        {
                            Flags = StateFlags.MoveTo | StateFlags.FillStyle0,
                            MoveDeltaX = 0,
                            MoveDeltaY = 0,
                            FillStyleIndex0 = 1
                        },
                        new StraightEdgeRecord { DeltaX = width, DeltaY = 0},
                        new StraightEdgeRecord { DeltaX = 0, DeltaY = height},
                        new StraightEdgeRecord { DeltaX = -width, DeltaY = 0},
                        new StraightEdgeRecord { DeltaX = 0, DeltaY = -height},
                        new EndShapeRecord()
                    }
                };
                tags.Add(tagShape);

                tags.Add(new RemoveObject2Tag { Depth = 50 });
                tags.Add(new PlaceObject2Tag
                {
                    Flags = PlaceFlags2.HasCharacter,
                    Depth = 50,
                    CharacterId = shapeId
                });
                tags.Add(new ShowFrameTag());
            }
            tags.Add(new EndTag());
            swf.AddTags(tags);

            // Write the file.
            swf.Save(stream);
        }
    }
}
